﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows.Input;

using ExchangeRateService.Command;
using ExchangeRateService.DataAccess;
using ExchangeRateService.Helpers;
using ExchangeRateService.Model;

namespace ExchangeRateService.ViewModel
{
	/// <summary>
	/// Description of CurrencyViewModel.
	/// </summary>
	public class CurrencyViewModel : WorkspaceViewModel, IDataErrorInfo, IEntityViewModel<Currency>
	{
		#region Fields
		
		Currency _currency;
		
		readonly CurrencyRepository _repository;
		
		Double _amount;
		
		ObservableCollection<String> _stringResults;
		
		Boolean _isSelected;
		
		Char[] SEPARATORS = new Char[]
		{
			',', 
			';', 
			'|'
		};

		#endregion // Fields
		
		#region Constants
		
		const String STRING_RESULT = "{0} {1} = {2} {3}";
		
		#endregion
		
		#region Constructor

		public CurrencyViewModel(Currency currency, CurrencyRepository repository)
		{
			if (repository == null)
				throw new ArgumentNullException("CurrencyRepository");

			_repository = repository;
			
			this.Entity = currency;
			
			_stringResults = new ObservableCollection<String>();
		}

		#endregion // Constructor
		
		#region Commands
		
		public ICommand ExchangeCommand
		{
			get { return new RelayCommand(param => ExecuteExchange(), param => CanExecuteExchange()); }
		}
		
		#endregion
		
		#region Currency Properties
		
		/// <summary>
        /// Gets or sets the item.
        /// </summary>
        /// <value>The item.</value>
        public virtual Currency Entity
        {
            get { return _currency; }
            set
            {
            	if (ReferenceEquals(_currency, value))
            		return;
            	
                _currency = value;
                RaisePropertyChanged(() => Entity);
            }
        }
		
		public String Code
		{
			get { return this.Entity.Code; }
			set
			{
				if (value == Entity.Code)
					return;

				Entity.Code = value;

				base.OnPropertyChanged("Code");
			}
		}
		
		public String Name
		{
			get { return this.Entity.Name; }
			set
			{
				if (value == Entity.Name)
					return;

				Entity.Name = value;

				base.OnPropertyChanged("Name");
			}
		}
		
		public Double USDRate
		{
			get { return this.Entity.USDRate; }
			set
			{
				if (value == Entity.USDRate)
					return;

				Entity.USDRate = value;

				base.OnPropertyChanged("USDRate");
			}
		}
		
		public Double CADRate
		{
			get { return this.Entity.CADRate; }
			set
			{
				if (value == Entity.CADRate)
					return;

				Entity.CADRate = value;

				base.OnPropertyChanged("CADRate");
			}
		}
		
		public Double EURRate
		{
			get { return this.Entity.EURRate; }
			set
			{
				if (value == Entity.EURRate)
					return;

				Entity.EURRate = value;

				base.OnPropertyChanged("EURRate");
			}
		}
		
		public Double GBPRate
		{
			get { return this.Entity.GBPRate; }
			set
			{
				if (value == Entity.GBPRate)
					return;

				Entity.GBPRate = value;

				base.OnPropertyChanged("GBPRate");
			}
		}
		
		public Double Amount
		{
			get { return _amount; }
			set
			{
				if (value == _amount)
					return;

				_amount = value;

				base.OnPropertyChanged("Amount");
			}
		}
		
		/// <summary>
		/// Gets the exchange result-set, and binds them to the list-box.
		/// 		Eg: 1 USD = 20.000 VND; 1 CAD = 19.000 VND...
		/// </summary>
		public ObservableCollection<String> StringResults
		{
			get { return _stringResults; }
			set
			{
				if (ReferenceEquals(_stringResults, value))
				    return;
				
				_stringResults = value;
				OnPropertyChanged("StringResults");
			}
		}
		
		public String SourceCurrencyCodes { get; set; }
		
		public String ForeignCurrencyCodes { get; set; }
		
		/// <summary>
		/// Gets/sets whether this customer is selected in the UI.
		/// </summary>
		public Boolean IsSelected
		{
			get { return _isSelected; }
			set
			{
				if (value == _isSelected)
					return;

				_isSelected = value;

				base.OnPropertyChanged("IsSelected");
			}
		}
		
		#endregion
		
		#region ViewModel Properties
		
		/// <summary>
		/// Manually gets the workspace-name for displaying in the UI.
		/// </summary>
		public override string DisplayName
		{
			get { return Properties.StringResource.SHOW_CURRENCY_EXCHANGE; }
		}
		
		#endregion
		
		#region Methods
		
		void ExecuteExchange()
		{
			if (!CanExecuteExchange()) return;
			
			Currency[] sources = _repository.GetCurrencies(SourceCurrencyCodes, SEPARATORS);
			Currency[] foreigns = _repository.GetCurrencies(ForeignCurrencyCodes, SEPARATORS);
			
			Exchange(sources, foreigns, Amount);
		}
		
		[DebuggerStepThrough]
		Boolean CanExecuteExchange()
		{
			return Amount > 0
				&& !RegexUtils.IsStringMissing(SourceCurrencyCodes)
				&& !RegexUtils.IsStringMissing(ForeignCurrencyCodes);
		}
		
		void Exchange(Currency[] sources, Currency[] foreigns, Double amount)
		{
			Int32 s, f;
			Int32 sl = sources.Length;
			Int32 fl = foreigns.Length;
			
			Currency cs, cf;
			
			// clear results in StringResults after each exchange...
			_stringResults.Clear();
			
			for (s = 0; s < sl; s++)
			{
				cs = sources[s];
				
				for (f = 0; f < fl; f++)
				{
					cf = foreigns[f];
					
					_stringResults.Add(GetResult(cs, cf));
				}
			}
		}
		
		String GetResult(Currency source, Currency foreign)
		{
			Double result = Math.Round(Amount * foreign.USDRate / source.USDRate, 5);
			return String.Format(STRING_RESULT, Amount, source.Code, result, foreign.Code);
		}
		
		protected override void OnDispose()
		{
			base.OnDispose();
		}

		
		#endregion
		
		#region IDataErrorInfo Members

		String IDataErrorInfo.Error
		{
			// I have no idea about what we can receive from here...
			get { return String.Empty; }
		}

		String IDataErrorInfo.this[String propertyName]
		{
			get
			{
				String error = String.Empty;

				if (propertyName == "Amount")
				{
					// The IsCompany property of the Customer class
					// is Boolean, so it has no concept of being in
					// an "unselected" state.  The CurrencyViewModel
					// class handles this mapping and validation.
					error = this.ValidateDouble();
				}
				else
				{
					// We only need to check the input-code  of the foreign currency.
					error = (this.Entity as IDataErrorInfo)[propertyName];
				}

				// Dirty the commands registered with CommandManager,
				// such as our Save command, so that they are queried
				// to see if they can execute now.
				CommandManager.InvalidateRequerySuggested();

				return error;
			}
		}

		String ValidateDouble()
		{
			String validation = String.Empty;
			
			if (!(RegexUtils.IsDoubleType(Amount.ToString())))
				validation = String.Format(Properties.StringResource.DOUBLETYPE_VALIDATION, Amount);
			
			if (Amount <= 0)
				validation = String.Format(Properties.StringResource.DOUBLETYPE_VALIDATION_GREATERTHANZERO, Amount);
			
			return validation;
		}

		#endregion // IDataErrorInfo Members
	}
}
